package com.catena.mock.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by hx-pc on 16-5-17.
 */
public class ScanContext implements ScanUrlContext, ScanDataContext, ScanContentContext{

    private static final Logger LOGGER = LoggerFactory.getLogger(ScanContext.class);
    public static final String FILE_NAME = "mockConfig";
    private String filePath;
    private String apiKey;
    private List<Map.Entry<String, String>> allKeyUrl = new CopyOnWriteArrayList<>();
    private Map<String, String> resourceUrlMap = new ConcurrentSkipListMap<>();
    private Map<String, Map<String, String>> resourceDataMap = new ConcurrentSkipListMap<>();
    private Map<String, Map<String, String>> resourceContentMap = new ConcurrentSkipListMap<>();

    private Map<String, Long> fileLastModified = new ConcurrentSkipListMap<>();
    private static Object object = new Object();

    public ScanContext(String projectName, String apiKey) throws IOException {
        filePath = "mockConfig" + projectName + "/";
        this.apiKey = apiKey;
        build();
    }

    private void build() throws IOException {
        init();
        load();
        scanLastModified();
    }

    private void init() {

        resourceDataMap.put("GET", new ConcurrentSkipListMap<>());
        resourceDataMap.put("POST", new ConcurrentSkipListMap<>());
        resourceDataMap.put("PUT", new ConcurrentSkipListMap<>());
        resourceDataMap.put("DELETE", new ConcurrentSkipListMap<>());

        resourceContentMap.put("GET", new ConcurrentSkipListMap<>());
        resourceContentMap.put("POST", new ConcurrentSkipListMap<>());
        resourceContentMap.put("PUT", new ConcurrentSkipListMap<>());
        resourceContentMap.put("DELETE", new ConcurrentSkipListMap<>());

    }

    private void scanLastModified() throws IOException {
        File rootDir = new File(filePath);
        File[] files = rootDir.listFiles();
        if (Objects.nonNull(files)) {
            for (File file : files) {
                fileLastModified.put(file.getName(), file.lastModified());
            }
        }
    }

    private void load() throws IOException {
        File rootDir = new File(filePath);
        if (!rootDir.exists()) {
            rootDir.mkdirs();
            LOGGER.info("创建目录mockConfig成功");
        }
        File file = new File(filePath + "/mockConfig.properties");
        if (!file.exists()) {
            file.createNewFile();
        }
        List<File> files = getFiles(rootDir);
        scanFile(files, true);
    }

    private List<File> getFiles(File rootDir) {
        List<File> list = new CopyOnWriteArrayList<>();
        for (String fileName : rootDir.list()) {
            if (fileName.endsWith("properties")) {
                list.add(new File(rootDir.getAbsolutePath() + "/" + fileName));
            }
        }
        return list;
    }

    public void scanFile(List<File> files, boolean isFirstScan) throws IOException {
        for (File file : files) {
            BufferedReader br = getBufferReader(file);
            if (!isFirstScan) {
                LOGGER.info("有文件被修改, 文件名 : {}", file.getName());
            }
            while (true) {
                String s = br.readLine();
                if (s != null) {
                    analyzeMockLineStr(new StringBuilder().append(s), isFirstScan);
                } else
                    break;
            }
            br.close();
        }
    }

    private BufferedReader getBufferReader(File file) throws UnsupportedEncodingException, FileNotFoundException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
        return new BufferedReader(isr);
    }


    private void analyzeMockLineStr(StringBuilder lineStr, boolean isFirstScan) {
        String str = lineStr.toString().replace("\n", "");
        if (str.length() > 0 && !str.startsWith("#")) {
            int indexPoint = str.indexOf('.');
            int indexEqual = str.indexOf('=') + 1;
            String beforePt = str.substring(indexPoint + 1, indexEqual - 1);
            String afterPt = str.substring(indexEqual);
            String apiKey = str.substring(0, indexPoint);
            buildUrlMap(apiKey, beforePt, afterPt, isFirstScan);
        }
    }

    private void buildUrlMap(String apiKey, String beforePt, String afterPt, boolean isFirstScan) {
        if (Objects.equals(apiKey, this.apiKey)) {
            if (isFirstScan) {
                allKeyUrl.add(new AbstractMap.SimpleEntry<>(beforePt, afterPt));
            } else {
                if (allKeyUrl.stream().allMatch(entry -> !(entry.getKey().equals(beforePt) && entry.getValue().equals(afterPt)))) {
                    allKeyUrl.add(new AbstractMap.SimpleEntry<>(beforePt, afterPt));
                }
            }
            resourceUrlMap.put(beforePt, afterPt);
        } else {
            if (!apiKey.contains("content")) {
                buildDataMap(apiKey.split("_")[1].toUpperCase(), beforePt, afterPt, isFirstScan);
            } else {
                buildContentMap(apiKey.split("_")[2].toUpperCase(), beforePt, afterPt);
            }
        }
    }

    private void buildDataMap(String httpMethod, String beforePt, String afterPt, boolean isFirstScan) {
        String info = "有api重复 : {}";
        Map<String, String> useMap = resourceDataMap.get(httpMethod);
        if (Objects.nonNull(useMap.get(resourceUrlMap.get(beforePt))) && isFirstScan) {
            LOGGER.info(info, resourceUrlMap.get(beforePt));
        }
        useMap.put(resourceUrlMap.get(beforePt), afterPt);
    }

    private void buildContentMap(String httpMethod, String beforePt, String afterPt) {
        Map<String, String> useMap = resourceContentMap.get(httpMethod);
        useMap.put(resourceUrlMap.get(beforePt), afterPt);
    }

    public String getDataWithApi(String requestURI, String method) {
        return getResourceDataMap().get(method.toUpperCase()).get(requestURI);
    }

    public String getContentWithApi(String requestURI, String method) {
        return getResourceContentMap().get(method.toUpperCase()).get(requestURI);
    }

    public Map<String, Long> getFileLastModified() {
        return fileLastModified;
    }

    public List<Map.Entry<String, String>> getAllKeyUrl() {
        return allKeyUrl;
    }

    public void checkScanMockData() throws IOException {
        File rootDir = new File(filePath);
        synchronized (object) {
            List<File> files = new CopyOnWriteArrayList<>();
            for (File file : rootDir.listFiles()) {
                validateLastModified(files, file);
            }
            if (!CollectionUtils.isEmpty(files)) {
                this.scanFile(files, false);
            }
        }
    }

    public void validateLastModified(List<File> files, File file) {
        Map<String, Long> fileLastModified = this.getFileLastModified();
        if (Objects.isNull(fileLastModified.get(file.getName()))) {
            files.add(file);
        } else {
            if (!(fileLastModified.get(file.getName()) - file.lastModified() == 0)) {
                fileLastModified.put(file.getName(), file.lastModified());
                files.add(file);
            }
        }
    }

    @Override
    public Map<String, Map<String, String>> getResourceContentMap() {
        return resourceContentMap;
    }

    @Override
    public Map<String, Map<String, String>> getResourceDataMap() {
        return resourceDataMap;
    }

    @Override
    public Map<String, String> getResourceUrlMap() {
        return resourceUrlMap;
    }

    public String getFileName() {
        return filePath + FILE_NAME + ".properties";
    }
}
